import { Link } from '@brillout/docpress'

## Basics

Permissions are implemented by using `throw Abort()` and `return`:

```ts
// TodoItem.telefunc.ts
// Environment: server

export { onTextChange }

import { getContext, Abort } from 'telefunc'

async function onTextChange(id: string, text: string) {
  const { user } = getContext()
  if (!user) {
    // We return `notLoggedIn: true` so that the frontend can redirect the user to the login page
    return { notLoggedIn: true }
  }

  const todoItem = await Todo.findOne({ id })
  if (!todoItem) {
    // `throw Abort()` corresponds to "403 Forbidden" of classical APIs
    throw Abort()
  }

  // We can easily programmatically implement advanced permissions such
  // as "only allow the author or admins to modify a to-do item".
  if (todoItem.authorId !== user.id && !user.isAdmin) {
    throw Abort()
  }
  await todoItem.update({ text })
}
```

In general, we use `throw Abort()` upon permission denials but, sometimes, the frontend needs to know why the the telefunction call failed:
in this example we return `{ notLoggedIn: true }` instead of `throw Abort()` so that the frontend can perform a redirection:

```tsx
// TodoItem.tsx
// Environment: client

import { onTextChange } from './TodoItem.telefunc'

async function onChange(id: string, text: string) {
  const res = await onTextChange(id, text)
  if (res?.notLoggedIn) {
    // Redirect user to login page
    window.location.href = '/login'
  }
}

function TodoItem({ id, text }: { id: string; text: string }) {
  return (
    <input input="text" value={text} onChange={async (ev) => await onChange(id, ev.target.value)} />
  )
}
```


## `getContext()` wrapping

To implement permission logic once and re-use it, we can define a `getContext()` wrapper:

```ts
// components/TodoItem.telefunc.ts
// Environment: server

export { onTextChange }

import { getUser } from '../auth/getUser'

function onTextChange(id: string, text: string) {
  const user = getUser()
  /* ... */
}
```

```ts
// auth/getUser.ts
// Environment: server

// Note that getUser() isn't a telefunction: it's a wrapper around getContext()
export { getUser }

import { getContext, Abort } from 'telefunc'

function getUser() {
  const { user } = getContext()
  if (!user) {
    throw Abort({ notLoggedIn: true })
  }
  return user
}
```

```ts
// Environment: client

import { onAbort } from 'telefunc/client'

onAbort((err) => {
  if (err.abortValue.notLoggedIn) {
    // Redirect user to login page
    window.location.href = '/login'
  }
})
```

## See also

 - <Link href="/onAbort" />
 - <Link href="/Abort" />
 - <Link href="/getContext" />
